//------------------------------------------------------------------------------
// SolutionItemIndex.cpp
//------------------------------------------------------------------------------
// File provided for Microsoft Shared Source.
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
// PARTICULAR PURPOSE.
//------------------------------------------------------------------------------
//
// The indexing and query engine for solution items
//
//------------------------------------------------------------------------------

#include "pch.h"

//------------------------------------------------------------------------------
// CSolutionItemTypeCache
//------------------------------------------------------------------------------

class ATL_NO_VTABLE CSolutionItemTypeCache :
    public CComObjectRootEx<CComSingleThreadModel>,
    public IObjectWithSiteImpl<CSolutionItemTypeCache>,
    public ISolutionItemTypeCache
{
public:
    typedef CAtlMap<CString, TYPECACHEINFO> CTypeMap;

    CSolutionItemTypeCache() : _himl(NULL) {}
    static HRESULT s_CreateInstance(REFIID riid, void **ppv);

    BEGIN_COM_MAP(CSolutionItemTypeCache)
        COM_INTERFACE_ENTRY(IObjectWithSite)
        COM_INTERFACE_ENTRY(ISolutionItemTypeCache)
    END_COM_MAP()

    DECLARE_PROTECT_FINAL_CONSTRUCT()

    virtual void FinalRelease();

    // ISolutionItemTypeCache
    IFACEMETHODIMP GetTypeInfo(PCWSTR pszCanonicalType, __out TYPECACHEINFO *ptci);
    IFACEMETHODIMP GetIconImageList(__out HIMAGELIST *phiml);

private:
    HRESULT _Initialize();

private:
    CTypeMap _mapTypes;
    HIMAGELIST _himl;
};

void CSolutionItemTypeCache::FinalRelease()
{
    if (_himl)
    {
        ImageList_Destroy(_himl);
    }
}

HRESULT CSolutionItemTypeCache::_Initialize()
{
    _himl = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 16, 8);
    return _himl ? S_OK : E_OUTOFMEMORY;
}

HRESULT CSolutionItemTypeCache::s_CreateInstance(REFIID riid, __deref_out void **ppv)
{
    *ppv = NULL;
    CComObjectWithRef<CSolutionItemTypeCache> *pspsitc;
    HRESULT hr = CComObjectWithRef<CSolutionItemTypeCache>::CreateInstance(&pspsitc);
    if (SUCCEEDED(hr))
    {
        hr = pspsitc->_Initialize();
        if (SUCCEEDED(hr))
        {
            hr = pspsitc->QueryInterface(riid, ppv);
        }
        pspsitc->Release();
    }
    return hr;
}

// ISolutionItemTypeCache
STDMETHODIMP CSolutionItemTypeCache::GetTypeInfo(PCWSTR pszCanonicalType, __out TYPECACHEINFO *ptci)
{
    HRESULT hr = E_FAIL;
    CTypeMap::CPair *pPair = _mapTypes.Lookup(pszCanonicalType);
    if (pPair)
    {
        ptci->iIconIndex = pPair->m_value.iIconIndex;
        hr = StringCchCopy(ptci->szFriendlyName, ARRAYSIZE(ptci->szFriendlyName), pPair->m_value.szFriendlyName);
    }
    else
    {
        SHFILEINFO shfi = {0};
        hr = SHGetFileInfo(pszCanonicalType, FILE_ATTRIBUTE_NORMAL, &shfi, sizeof(shfi), SHGFI_ICON | SHGFI_SMALLICON | SHGFI_SHELLICONSIZE | SHGFI_TYPENAME | SHGFI_USEFILEATTRIBUTES) ? S_OK : E_FAIL;
        if (SUCCEEDED(hr))
        {
            TYPECACHEINFO tci = {0};
            tci.iIconIndex = ImageList_ReplaceIcon(_himl, -1, shfi.hIcon);
            hr = tci.iIconIndex != -1 ? S_OK : E_FAIL;
            if (SUCCEEDED(hr))
            {
                hr = StringCchCopy(tci.szFriendlyName, ARRAYSIZE(tci.szFriendlyName), shfi.szTypeName);
                if (SUCCEEDED(hr))
                {
                    try
                    {
                        _mapTypes.SetAt(pszCanonicalType, tci);
                    }
                    catch (CAtlException &e)
                    {
                        hr = e.m_hr;
                    }

                    if (SUCCEEDED(hr))
                    {
                        ptci->iIconIndex = tci.iIconIndex;
                        hr = StringCchCopy(ptci->szFriendlyName, ARRAYSIZE(ptci->szFriendlyName), tci.szFriendlyName);
                    }
                }
            }
            DestroyIcon(shfi.hIcon);
        }
    }
    return hr;
}

STDMETHODIMP CSolutionItemTypeCache::GetIconImageList(__out HIMAGELIST *phiml)
{
    *phiml = _himl;
    return S_OK;
}

//------------------------------------------------------------------------------
// CSolutionItemIndex
//------------------------------------------------------------------------------

struct INDEXENTRY
{
    INDEXENTRYTYPE iet;
    CComPtr<ISolutionItem> spItem;
};

class ATL_NO_VTABLE CSolutionItemIndex :
    public CComObjectRootEx<CComSingleThreadModel>,
    public IObjectWithSiteImpl<CSolutionItemIndex>,
    public IServiceProviderImpl<CSolutionItemIndex>,
	public IConnectionPointContainerImpl<CSolutionItemIndex>,
	public IConnectionPointImpl<CSolutionItemIndex, &__uuidof(ISolutionItemIndexEvents), CComDynamicUnkArray>,
    public ISolutionItemIndex
{
public:
    CSolutionItemIndex();

    static HRESULT s_CreateInstance(REFIID riid, __deref_out void **ppv);

    DECLARE_PROTECT_FINAL_CONSTRUCT()
    virtual HRESULT FinalConstruct();

    BEGIN_COM_MAP(CSolutionItemIndex)
        COM_INTERFACE_ENTRY(IObjectWithSite)
        COM_INTERFACE_ENTRY(IServiceProvider)
        COM_INTERFACE_ENTRY(ISolutionItemIndex)
    	COM_INTERFACE_ENTRY(IConnectionPointContainer)
    END_COM_MAP()

    BEGIN_SERVICE_MAP(CSolutionItemIndex)
        SERVICE_ENTRY_FWD(SID_SSolutionItemTypeCache, _spsitc)
    END_SERVICE_MAP()

	BEGIN_CONNECTION_POINT_MAP(CSolutionItemIndex)
		CONNECTION_POINT_ENTRY(__uuidof(ISolutionItemIndexEvents))
	END_CONNECTION_POINT_MAP()

    // IObjectWithSite
    IFACEMETHODIMP SetSite(__in IUnknown *punkSite);

    // ISolutionItemIndex
    IFACEMETHODIMP Search(PCWSTR pszSearch, __in INDEXQUERYPARAMS *piqp, REFIID riid, __deref_out void **ppv);

private:
    typedef CAtlMap<CString, INDEXENTRYTYPE, CStringElementTraitsI<CString>> CPathPartMap;
    typedef CRBMultiMap<CString, INDEXENTRY, CStringElementTraitsI<CString>> CSolutionItemMap;
    typedef IConnectionPointImpl<CSolutionItemIndex, &__uuidof(ISolutionItemIndexEvents), CComDynamicUnkArray> ISolutionItemIndexConnectionPoint;

    enum INDEXOPERATION
    {
        IOP_ADD = 1,
        IOP_REMOVE = 2,
    };
    HRESULT _BuildFromSolution(__in _Solution *pvsSolution);
    HRESULT _ProcessProject(__in Project *pvsProject, INDEXOPERATION iop);
    HRESULT _ProcessProjectItems(__in ProjectItems *pvsProjectItems, INDEXOPERATION iop);
    HRESULT _ProcessProjectItem(__in ProjectItem *pvsProjectItem, INDEXOPERATION iop);
    HRESULT _ProcessFileItem(__in ProjectItem *pvsProjectItem, INDEXOPERATION iop);
    HRESULT _AddSolutionItemToMap(__in ISolutionItem *psi);
    HRESULT _RemoveSolutionItemFromMap(PCWSTR pszPath);
    HRESULT _FireIndexChanged();
    HRESULT _CreateCategorizer(__in INDEXQUERYPARAMS *piqp, REFIID riid, __deref_out void **ppv);

    class CSolutionEventSink :
        public CDTEEventSinkBase<1, CSolutionEventSink, _dispSolutionEvents>
    {
    public:
        CSolutionEventSink() {}
        ~CSolutionEventSink() {}

        BEGIN_SINK_MAP(CSolutionEventSink)
            SINK_ENTRY_EX(1, __uuidof(_dispSolutionEvents), 1, Opened)
            SINK_ENTRY_EX(1, __uuidof(_dispSolutionEvents), 2, BeforeClosing)
            SINK_ENTRY_EX(1, __uuidof(_dispSolutionEvents), 6, ProjectAdded)
            SINK_ENTRY_EX(1, __uuidof(_dispSolutionEvents), 7, ProjectRemoved)
        END_SINK_MAP()

        IFACEMETHODIMP Opened()
        {
            GET_EMBEDDED_PARENT(CSolutionItemIndex, x_snkSolution, pfsec);
            pfsec->_SolutionOpened();
            return S_OK;
        }

        IFACEMETHODIMP BeforeClosing()
        {
            GET_EMBEDDED_PARENT(CSolutionItemIndex, x_snkSolution, pfsec);
            pfsec->_BeforeClosing();
            return S_OK;
        }

        IFACEMETHODIMP ProjectAdded(__in Project *pvsProject)
        {
            GET_EMBEDDED_PARENT(CSolutionItemIndex, x_snkSolution, pfsec);
            pfsec->_ProjectAdded(pvsProject);
            return S_OK;
        }

        IFACEMETHODIMP ProjectRemoved(__in Project *pvsProject)
        {
            GET_EMBEDDED_PARENT(CSolutionItemIndex, x_snkSolution, pfsec);
            pfsec->_ProjectRemoved(pvsProject);
            return S_OK;
        }

    protected:
        virtual HRESULT GetEventSourceObject(__in Events2 *pvsEvents2, REFIID riid, __deref_out void **ppv)
        {
            CComPtr<_SolutionEvents> spvsSolutionEvents;
            HRESULT hr = pvsEvents2->get_SolutionEvents(&spvsSolutionEvents);
            if (SUCCEEDED(hr))
            {
                hr = spvsSolutionEvents->QueryInterface(riid, ppv);
            }
            return hr;
        }

    } x_snkSolution;

    HRESULT _SolutionOpened();
    void _BeforeClosing();
    void _ProjectAdded(__in Project *pvsProject);
    void _ProjectRemoved(__in Project *pvsProject);

    class CProjectItemsEventSink :
        public CDTEEventSinkBase<2, CProjectItemsEventSink, _dispProjectItemsEvents>
    {
    public:
        CProjectItemsEventSink() {}
        ~CProjectItemsEventSink() {}

        BEGIN_SINK_MAP(CProjectItemsEventSink)
            SINK_ENTRY_EX(2, __uuidof(_dispProjectItemsEvents), 1, ItemAdded)
            SINK_ENTRY_EX(2, __uuidof(_dispProjectItemsEvents), 2, ItemRemoved)
        END_SINK_MAP()

        IFACEMETHODIMP ItemAdded(__in ProjectItem *pvsProjectItem)
        {
            GET_EMBEDDED_PARENT(CSolutionItemIndex, x_snkProjectItems, pfsec);
            pfsec->_ItemAdded(pvsProjectItem);
            return S_OK;
        }

        IFACEMETHODIMP ItemRemoved(__in ProjectItem *pvsProjectItem)
        {
            GET_EMBEDDED_PARENT(CSolutionItemIndex, x_snkProjectItems, pfsec);
            pfsec->_ItemRemoved(pvsProjectItem);
            return S_OK;
        }

    protected:
        virtual HRESULT GetEventSourceObject(__in Events2 *pvsEvents2, REFIID riid, __deref_out void **ppv)
        {
            CComPtr<_ProjectItemsEvents> spvsProjectItemsEvents;
            HRESULT hr = pvsEvents2->get_ProjectItemsEvents(&spvsProjectItemsEvents);
            if (SUCCEEDED(hr))
            {
                hr = spvsProjectItemsEvents->QueryInterface(riid, ppv);
            }
            return hr;
        }
    } x_snkProjectItems;

    void _ItemAdded(__in ProjectItem *pvsProjectItem);
    void _ItemRemoved(__in ProjectItem *pvsProjectItem);

private:
    CComPtr<DTE2> _spvsDTE2;
    CSolutionItemMap _mapCache;
    CComPtr<ISolutionItemTypeCache> _spsitc;
};

HRESULT CSolutionItemIndex::s_CreateInstance(REFIID riid, __deref_out void **ppv)
{
    *ppv = NULL;
    CComObjectWithRef<CSolutionItemIndex> *pspsii;
    HRESULT hr = CComObjectWithRef<CSolutionItemIndex>::CreateInstance(&pspsii);
    if (SUCCEEDED(hr))
    {
        hr = pspsii->QueryInterface(riid, ppv);
        pspsii->Release();
    }
    return hr;
}

STDAPI CreateSolutionItemIndex(REFIID riid, __deref_out void **ppv)
{
    return CSolutionItemIndex::s_CreateInstance(riid, ppv);
}

CSolutionItemIndex::CSolutionItemIndex()
{
}

HRESULT CSolutionItemIndex::FinalConstruct()
{
    return CSolutionItemTypeCache::s_CreateInstance(IID_PPV_ARGS(&_spsitc));
}

// IObjectWithSite
STDMETHODIMP CSolutionItemIndex::SetSite(__in IUnknown *punkSite)
{
    x_snkSolution.Disconnect();

    IObjectWithSiteImpl<CSolutionItemIndex>::SetSite(punkSite);

    if (m_spUnkSite)
    {
        CComPtr<IServiceProvider> spsp;
        if (SUCCEEDED(m_spUnkSite->QueryInterface(&spsp)))
        {
            if (SUCCEEDED(spsp->QueryService(SID_SFseAddIn, &_spvsDTE2)))
            {
                x_snkSolution.Connect(_spvsDTE2);
                _SolutionOpened();
            }
        }
    }
    return S_OK;
}

// ISolutionItemCache

HRESULT CSolutionItemIndex::_CreateCategorizer(__in INDEXQUERYPARAMS *piqp, REFIID riid, __deref_out void **ppv)
{
    const CLSID *pclsidCategorizer = &CLSID_NULL;
    if (piqp->colidGroup == COLID_NONE)
    {
        pclsidCategorizer = &__uuidof(NullCategorizer);
    }
    else
    {
        pclsidCategorizer = &s_rgColumns[piqp->colidGroup].clsidCategorizer;
    }

    CComPtr<ISolutionItemCategorizer> spsic;
    HRESULT hr = spsic.CoCreateInstance(*pclsidCategorizer);
    if (SUCCEEDED(hr))
    {
        hr = spsic->Initialize(piqp->colidSort, piqp->fSortAscending);
        if (SUCCEEDED(hr))
        {
            hr = spsic->QueryInterface(riid, ppv);
        }
    }
    return hr;
}

STDMETHODIMP CSolutionItemIndex::Search(PCWSTR pszSearch, __in INDEXQUERYPARAMS *piqp, REFIID riid, __deref_out void **ppv)
{
    BOOL fFilter = pszSearch && *pszSearch;
    POSITION pos;
    size_t cchMatch = 0;

    if (fFilter)
    {
        cchMatch = wcslen(pszSearch);
        pos = _mapCache.FindFirstWithKey(pszSearch);
        if (!pos)
        {
            pos = _mapCache.FindFirstKeyAfter(pszSearch);
        }
    }
    else
    {
        pos = _mapCache.GetHeadPosition();
    }

    CComPtr<ISolutionItemCategorizer> spsic;
    HRESULT hr = _CreateCategorizer(piqp, IID_PPV_ARGS(&spsic));
    for (BOOL fContinue = TRUE; SUCCEEDED(hr) && pos && fContinue;)
    {
        CSolutionItemMap::CPair *pMatch = _mapCache.GetNext(pos);
        if (fFilter)
        {
            fContinue = StrCmpNI(pszSearch, pMatch->m_key, (int)cchMatch) == 0;
        }
        if (fContinue)
        {
            // If there is no filter, it means we'll be returning all files in the cache.  This check
            // slightly optimizes that case so that we don't have to do all the duplicate removal for
            // the path part hits.
            if (fFilter || pMatch->m_value.iet == IET_FILENAME)
            {
                hr = spsic->Categorize(pMatch->m_value.iet, pMatch->m_value.spItem);
            }
        }
    }
    if (SUCCEEDED(hr))
    {
        hr = spsic->GetCategories(riid, ppv);
    }

    return hr;
}

HRESULT CSolutionItemIndex::_FireIndexChanged()
{
    ISolutionItemIndexConnectionPoint *pcp = this;
    Lock();
    HRESULT hr = S_OK;
    IUnknown** pp = pcp->m_vec.begin();
    while (pp < pcp->m_vec.end() && hr == S_OK)
    {
        if (*pp != NULL)
        {
            ISolutionItemIndexEvents *psiie = (ISolutionItemIndexEvents*)*pp;
            hr = psiie->IndexUpdated();
        }
        pp++;
    }
    Unlock();
    return hr;
}

HRESULT CSolutionItemIndex::_AddSolutionItemToMap(__in ISolutionItem *psi)
{
    WCHAR szPath[MAX_PATH];
    HRESULT hr = psi->GetPath(szPath, ARRAYSIZE(szPath));
    if (SUCCEEDED(hr))
    {
        CPathPartMap mapParts;
        WCHAR szPart[MAX_PATH];
        PWSTR pszPathPart = szPath;
        PWSTR pszStart = szPath;
        try
        {
            while ((pszPathPart = PathFindNextComponent(pszPathPart)) != NULL && SUCCEEDED(hr))
            {
                size_t cch = pszPathPart - pszStart;
                hr = StringCchCopyN(szPart, ARRAYSIZE(szPart), pszStart, cch);
                if (SUCCEEDED(hr))
                {
                    if (szPart[cch-1] == L'\\')
                    {
                        szPart[cch-1] = 0;
                    }
                    hr = mapParts.SetAt(szPart, IET_PATHPART) ? S_OK : E_OUTOFMEMORY;
                }
                pszStart = pszPathPart;
            }

            // Add the file name
            if (SUCCEEDED(hr))
            {
                WCHAR szName[MAX_PATH];
                hr = psi->GetName(szName, ARRAYSIZE(szName));
                if (SUCCEEDED(hr))
                {
                    hr = mapParts.SetAt(szName, IET_FILENAME) ? S_OK : E_OUTOFMEMORY;
                }
            }

            if (SUCCEEDED(hr))
            {
                POSITION pos = mapParts.GetStartPosition();
                while (pos)
                {
                    CPathPartMap::CPair *pPair = mapParts.GetNext(pos);

                    INDEXENTRY ie = { pPair->m_value, psi };
                    _mapCache.Insert(pPair->m_key, ie);
                }
            }
        }
        catch (CAtlException &e)
        {
            hr = e.m_hr;
        }
    }
    return hr;
}

HRESULT CSolutionItemIndex::_RemoveSolutionItemFromMap(PCWSTR pszPath)
{
    HRESULT hr = S_OK;
    POSITION pos = _mapCache.GetHeadPosition();
    while (pos && SUCCEEDED(hr))
    {
        POSITION posExamine = pos;
        CSolutionItemMap::CPair *pMatch = _mapCache.GetNext(pos);

        WCHAR szItemPath[MAX_PATH];
        hr = pMatch->m_value.spItem->GetFullPath(szItemPath, ARRAYSIZE(szItemPath));
        if (SUCCEEDED(hr))
        {
            if (StrCmpI(pszPath, szItemPath) == 0)
            {
                _mapCache.RemoveAt(posExamine);
            }
        }
    }
    return hr;
}

HRESULT CSolutionItemIndex::_ProcessFileItem(__in ProjectItem *pvsProjectItem, INDEXOPERATION iop)
{
    HRESULT hr = E_FAIL;
    if (iop == IOP_ADD)
    {
        CComPtr<ISolutionItem> spSolutionItem;
        hr = CreateSolutionItem(pvsProjectItem, _spsitc, IID_PPV_ARGS(&spSolutionItem));
        if (SUCCEEDED(hr))
        {
            hr = _AddSolutionItemToMap(spSolutionItem);
        }
    }
    else
    {
        CComBSTR sbstrPath;
        hr = pvsProjectItem->get_FileNames(0, &sbstrPath);
        if (SUCCEEDED(hr))
        {
            hr = _RemoveSolutionItemFromMap(sbstrPath);
        }
    }
    return hr;
}

HRESULT CSolutionItemIndex::_ProcessProjectItem(__in ProjectItem *pvsProjectItem, INDEXOPERATION iop)
{
    CComBSTR sbstrKind;
    HRESULT hr = pvsProjectItem->get_Kind(&sbstrKind);
    if (SUCCEEDED(hr))
    {
        if (sbstrKind == vsProjectItemKindPhysicalFile)
        {
            hr = _ProcessFileItem(pvsProjectItem, iop);
            CComPtr<ProjectItems> spvsProjectItems;
            hr = pvsProjectItem->get_ProjectItems(&spvsProjectItems);
            if (SUCCEEDED(hr))
            {
                hr = _ProcessProjectItems(spvsProjectItems, iop);
            }
        }
        else if (sbstrKind == vsProjectItemKindVirtualFolder)
        {
            CComPtr<ProjectItems> spvsProjectItems;
            hr = pvsProjectItem->get_ProjectItems(&spvsProjectItems);
            if (SUCCEEDED(hr))
            {
                hr = _ProcessProjectItems(spvsProjectItems, iop);
            }
        }
        else if (sbstrKind == vsProjectItemKindPhysicalFolder)
        {
            CComPtr<ProjectItems> spvsProjectItems;
            hr = pvsProjectItem->get_ProjectItems(&spvsProjectItems);
            if (SUCCEEDED(hr))
            {
                hr = _ProcessProjectItems(spvsProjectItems, iop);
            }
        }
        else if (sbstrKind == vsProjectItemKindSolutionItems)
        {
            CComPtr<Project> spvsProject;
            hr = pvsProjectItem->get_SubProject(&spvsProject);
            if (SUCCEEDED(hr) && spvsProject)
            {
                hr = _ProcessProject(spvsProject, iop);
            }
        }
        else
        {
            hr = S_FALSE;
        }
    }
    return hr;
}

HRESULT CSolutionItemIndex::_ProcessProjectItems(__in ProjectItems *pvsProjectItems, INDEXOPERATION iop)
{
    long cItems;
    HRESULT hr = pvsProjectItems->get_Count(&cItems);
    for (long i = 1; i <= cItems && SUCCEEDED(hr); i++)
    {
        CComPtr<ProjectItem> spvsProjectItem;
        CComVariant svarItem(i);
        hr = pvsProjectItems->Item(svarItem, &spvsProjectItem); // One based
        if (SUCCEEDED(hr))
        {
            hr = _ProcessProjectItem(spvsProjectItem, iop);
        }
    }
    return hr;
}

HRESULT CSolutionItemIndex::_ProcessProject(__in Project *pvsProject, INDEXOPERATION iop)
{
    CComPtr<ProjectItems> spvsProjectItems;
    HRESULT hr = pvsProject->get_ProjectItems(&spvsProjectItems);
    if (SUCCEEDED(hr) && spvsProjectItems)
    {
        hr = _ProcessProjectItems(spvsProjectItems, iop);
    }
    return hr;
}

HRESULT CSolutionItemIndex::_BuildFromSolution(__in _Solution *pvsSolution)
{
    _mapCache.RemoveAll();

    CComPtr<Projects> spvsProjects;
    HRESULT hr = pvsSolution->get_Projects(&spvsProjects);
    if (SUCCEEDED(hr))
    {
        long cProjects;
        hr = spvsProjects->get_Count(&cProjects);
        for (long i = 1; i <= cProjects && SUCCEEDED(hr); i++)
        {
            CComPtr<Project> spvsProject;
            CComVariant svarProject(i);
            hr = spvsProjects->Item(svarProject, &spvsProject); // One based
            if (SUCCEEDED(hr))
            {
                hr = _ProcessProject(spvsProject, IOP_ADD);
            }
        }
    }
    return hr;
}

HRESULT CSolutionItemIndex::_SolutionOpened()
{
    CComPtr<_Solution> spvsSolution;
    HRESULT hr = _spvsDTE2->get_Solution(&spvsSolution);
    if (SUCCEEDED(hr))
    {
        hr = _BuildFromSolution(spvsSolution);
        if (SUCCEEDED(hr))
        {
            hr = x_snkProjectItems.Connect(_spvsDTE2);
            _FireIndexChanged();
        }
    }
    return hr;
}

void CSolutionItemIndex::_BeforeClosing()
{
    x_snkProjectItems.Disconnect();
    _mapCache.RemoveAll();
    _FireIndexChanged();
}

void CSolutionItemIndex::_ProjectAdded(__in Project *pvsProject)
{
    if (SUCCEEDED(_ProcessProject(pvsProject, IOP_ADD)))
    {
        _FireIndexChanged();
    }
}

void CSolutionItemIndex::_ProjectRemoved(__in Project *pvsp)
{
    if (SUCCEEDED(_ProcessProject(pvsp, IOP_REMOVE)))
    {
        _FireIndexChanged();
    }
}

void CSolutionItemIndex::_ItemAdded(__in ProjectItem *pvsProjectItem)
{
    if (SUCCEEDED(_ProcessProjectItem(pvsProjectItem, IOP_ADD)))
    {
        _FireIndexChanged();
    }
}

void CSolutionItemIndex::_ItemRemoved(__in ProjectItem *pvsProjectItem)
{
    if (SUCCEEDED(_ProcessProjectItem(pvsProjectItem, IOP_REMOVE)))
    {
        _FireIndexChanged();
    }
}
